use crate::{cmd::cargo, error::AppErr, task::handler_task::Response};
use proto_bytes::message::ReqRef;
use utils::bincodec::Decode;

use super::driver::{self, cargo_motor, compr, cy_reset, pick_motor, scan_uart, sensor};

pub fn on_simple_req(req: ReqRef) -> Result<Response, AppErr> {
    let cmd = req.cmd();

    match cmd {
        cargo::SCAN_LED_CTRL => req
            .parse()
            .ok_or(AppErr::Parse)
            .and_then(|req| on_scan_led_ctrl(req)),

        _ => Err(AppErr::InvalidCmd),
    }
}

pub fn on_request(req: ReqRef) -> Result<Response, AppErr> {
    let cmd = req.cmd();

    match cmd {
        cargo::PICK_CTRL => req
            .parse()
            .ok_or(AppErr::Parse)
            .and_then(|req| on_pick_ctrl(req)),

        cargo::COMPR_CTRL => req
            .parse()
            .ok_or(AppErr::Parse)
            .and_then(|req| on_compr_ctrl(req)),

        cargo::CARGO_CTRL => req
            .parse()
            .ok_or(AppErr::Parse)
            .and_then(|req| on_cargo_ctrl(req)),

        cargo::ROW_COL_CTRL => req
            .parse_with_decode()
            .ok_or(AppErr::Parse)
            .and_then(|req| on_row_col_ctrl(req)),

        cargo::SCAN => on_scan(),

        _ => Err(AppErr::InvalidCmd),
    }
}

pub fn on_idle() {
    if !sensor::is_rotation_button_click() {
        return;
    }
    if cy_reset().is_err() {
        return;
    }
    cargo_motor::auto_run();
}

#[derive(Decode)]
struct RowCol {
    row: i32,
    col: u32,
}

fn on_row_col_ctrl(req: RowCol) -> Result<Response, AppErr> {
    if req.col >= pick_motor::MAX_PULS_COUNT {
        return Err(AppErr::Invalid);
    }
    driver::cargo_pick_motor_run(req.row, req.col)?;
    Ok(Response::Empty(()))
}

fn on_scan_led_ctrl(ctrl: u8) -> Result<Response, AppErr> {
    match ctrl {
        0 => compr::led_open(),
        1 => compr::led_close(),
        _ => return Err(AppErr::Invalid),
    }
    Ok(Response::Empty(()))
}

fn on_scan() -> Result<Response, AppErr> {
    let body = scan_uart::scan()?;
    Ok(Response::Raw(body))
}

fn on_pick_ctrl(position: u32) -> Result<Response, AppErr> {
    if position >= pick_motor::MAX_PULS_COUNT {
        return Err(AppErr::Invalid);
    }
    pick_motor::run(position)?;
    Ok(Response::Empty(()))
}

fn on_compr_ctrl(position: u8) -> Result<Response, AppErr> {
    match position {
        0 => compr::cy_close(),
        1 => compr::cy_to_top()?,
        2 => compr::cy_to_bottom()?,
        3 => compr::cy_to_inner()?,
        4 => compr::cy_to_extern()?,
        5 => compr::air_open(),
        6 => compr::air_drop(),
        7 => compr::air_close(),
        8 => compr::cy_run_pick()?,
        _ => return Err(AppErr::Invalid),
    };
    Ok(Response::Empty(()))
}

fn on_cargo_ctrl(position: i32) -> Result<Response, AppErr> {
    cy_reset()?;
    cargo_motor::run(position)?;
    Ok(Response::Empty(()))
}
